gtk: Add GtkEventControllerKey
authorCarlos Garnacho <carlosg@gnome.org>
Mon, 11 Dec 2017 18:21:38 +0000 (19:21 +0100)
committerCarlos Garnacho <carlosg@gnome.org>
Mon, 16 Jul 2018 13:43:43 +0000 (15:43 +0200)
This event controller is meant to replace usage from key-press/release-event
handlers all through. Optionally it can be set a GtkIMContext, so interaction
is carried by the controller.

gtk/Makefile.am
gtk/gtk.h
gtk/gtkeventcontrollerkey.c [new file with mode: 0644]
gtk/gtkeventcontrollerkey.h [new file with mode: 0644]
po/POTFILES.in

index ec81f395d763d973b1cbd69ccffb41dd89fd66a1..93f3d5c29c4a165b51aea73d80cebf4505cbb84e 100644 (file)
@@ -192,6 +192,7 @@ gtk_public_h_sources =              \
        gtkenums.h              \
        gtkeventbox.h           \
        gtkeventcontroller.h    \
+       gtkeventcontrollerkey.h \
        gtkeventcontrollermotion.h      \
        gtkeventcontrollerscroll.h      \
        gtkexpander.h           \
@@ -759,6 +760,7 @@ gtk_base_c_sources =                \
        gtkentrycompletion.c    \
        gtkeventbox.c           \
        gtkeventcontroller.c    \
+       gtkeventcontrollerkey.c \
        gtkeventcontrollermotion.c      \
        gtkeventcontrollerscroll.c      \
        gtkexpander.c           \
index 46cfbfc063ab4a26d9798bff40ba34bc8d40eff7..8dee2a9efc599b52728b5c50badc4ffce382cdb5 100644 (file)
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -94,6 +94,7 @@
 #include <gtk/gtkenums.h>
 #include <gtk/gtkeventbox.h>
 #include <gtk/gtkeventcontroller.h>
+#include <gtk/gtkeventcontrollerkey.h>
 #include <gtk/gtkeventcontrollermotion.h>
 #include <gtk/gtkeventcontrollerscroll.h>
 #include <gtk/gtkexpander.h>
diff --git a/gtk/gtkeventcontrollerkey.c b/gtk/gtkeventcontrollerkey.c
new file mode 100644 (file)
index 0000000..1301531
--- /dev/null
@@ -0,0 +1,217 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s): Carlos Garnacho <carlosg@gnome.org>
+ */
+
+#include "config.h"
+
+#include "gtkintl.h"
+#include "gtkprivate.h"
+#include "gtkwidget.h"
+#include "gtkeventcontrollerprivate.h"
+#include "gtkeventcontrollerkey.h"
+#include "gtkbindings.h"
+
+#include <gdk/gdk.h>
+
+struct _GtkEventControllerKey
+{
+  GtkEventController parent_instance;
+  GtkIMContext *im_context;
+  GHashTable *pressed_keys;
+
+  const GdkEvent *current_event;
+};
+
+struct _GtkEventControllerKeyClass
+{
+  GtkEventControllerClass parent_class;
+};
+
+enum {
+  KEY_PRESSED,
+  KEY_RELEASED,
+  MODIFIERS,
+  IM_UPDATE,
+  N_SIGNALS
+};
+
+static guint signals[N_SIGNALS] = { 0 };
+
+G_DEFINE_TYPE (GtkEventControllerKey, gtk_event_controller_key,
+               GTK_TYPE_EVENT_CONTROLLER)
+
+static void
+gtk_event_controller_finalize (GObject *object)
+{
+  GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (object);
+
+  g_hash_table_destroy (key->pressed_keys);
+  g_clear_object (&key->im_context);
+
+  G_OBJECT_CLASS (gtk_event_controller_key_parent_class)->finalize (object);
+}
+
+static gboolean
+gtk_event_controller_key_handle_event (GtkEventController *controller,
+                                       const GdkEvent     *event)
+{
+  GtkEventControllerKey *key = GTK_EVENT_CONTROLLER_KEY (controller);
+  GdkEventType event_type = gdk_event_get_event_type (event);
+  gboolean handled;
+  GdkModifierType state;
+  guint16 keycode;
+  guint keyval;
+
+  if (event_type != GDK_KEY_PRESS && event_type != GDK_KEY_RELEASE)
+    return FALSE;
+
+  if (key->im_context &&
+      gtk_im_context_filter_keypress (key->im_context, (GdkEventKey *) event))
+    {
+      g_signal_emit (controller, signals[IM_UPDATE], 0);
+      return TRUE;
+    }
+
+  if (!gdk_event_get_state (event, &state) || !event->key.is_modifier)
+    return FALSE;
+
+  key->current_event = event;
+
+  if (event->key.is_modifier)
+    {
+      if (event_type == GDK_KEY_PRESS)
+        g_signal_emit (controller, signals[MODIFIERS], 0, state, &handled);
+      else
+        handled = TRUE;
+
+      if (handled == TRUE)
+        {
+          key->current_event = NULL;
+          return TRUE;
+        }
+    }
+
+  gdk_event_get_keycode (event, &keycode);
+  gdk_event_get_keyval (event, &keyval);
+
+  if (event_type == GDK_KEY_PRESS)
+    {
+      g_signal_emit (controller, signals[KEY_PRESSED], 0,
+                     keyval, keycode, state, &handled);
+      if (handled)
+        g_hash_table_add (key->pressed_keys, GUINT_TO_POINTER (keyval));
+    }
+  else if (event_type == GDK_KEY_RELEASE)
+    {
+      g_signal_emit (controller, signals[KEY_RELEASED], 0,
+                     keyval, keycode, state);
+
+      handled = g_hash_table_lookup (key->pressed_keys, GUINT_TO_POINTER (keyval)) != NULL;
+      g_hash_table_remove (key->pressed_keys, GUINT_TO_POINTER (keyval));
+    }
+  else
+    handled = FALSE;
+
+  key->current_event = NULL;
+
+  return handled;
+}
+
+static void
+gtk_event_controller_key_class_init (GtkEventControllerKeyClass *klass)
+{
+  GtkEventControllerClass *controller_class = GTK_EVENT_CONTROLLER_CLASS (klass);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gtk_event_controller_finalize;
+  controller_class->handle_event = gtk_event_controller_key_handle_event;
+
+  signals[KEY_PRESSED] =
+    g_signal_new (I_("key-pressed"),
+                  GTK_TYPE_EVENT_CONTROLLER_KEY,
+                  G_SIGNAL_RUN_LAST,
+                  0, _gtk_boolean_handled_accumulator, NULL, NULL,
+                  G_TYPE_BOOLEAN, 3, G_TYPE_UINT, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE);
+  signals[KEY_RELEASED] =
+    g_signal_new (I_("key-released"),
+                  GTK_TYPE_EVENT_CONTROLLER_KEY,
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL, NULL,
+                  G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, GDK_TYPE_MODIFIER_TYPE);
+  signals[MODIFIERS] =
+    g_signal_new (I_("modifiers"),
+                  GTK_TYPE_EVENT_CONTROLLER_KEY,
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_BOOLEAN__FLAGS,
+                  G_TYPE_BOOLEAN, 1, GDK_TYPE_MODIFIER_TYPE);
+  signals[IM_UPDATE] =
+    g_signal_new (I_("im-update"),
+                  GTK_TYPE_EVENT_CONTROLLER_KEY,
+                  G_SIGNAL_RUN_LAST,
+                  0, NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+}
+
+static void
+gtk_event_controller_key_init (GtkEventControllerKey *controller)
+{
+  controller->pressed_keys = g_hash_table_new (NULL, NULL);
+}
+
+GtkEventController *
+gtk_event_controller_key_new (GtkWidget *widget)
+{
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+  return g_object_new (GTK_TYPE_EVENT_CONTROLLER_KEY,
+                       "widget", widget,
+                       NULL);
+}
+
+void
+gtk_event_controller_key_set_im_context (GtkEventControllerKey *controller,
+                                         GtkIMContext          *im_context)
+{
+  g_return_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller));
+  g_return_if_fail (!im_context || GTK_IS_IM_CONTEXT (im_context));
+
+  if (controller->im_context)
+    gtk_im_context_reset (controller->im_context);
+
+  g_set_object (&controller->im_context, im_context);
+}
+
+/**
+ * gtk_event_controller_key_get_im_context:
+ * @controller: a #GtkEventControllerKey
+ *
+ * Gets the IM context of a key controller.
+ *
+ * Returns: (transfer none): the IM context
+ *
+ * Since: 3.94
+ **/
+GtkIMContext *
+gtk_event_controller_key_get_im_context (GtkEventControllerKey *controller)
+{
+  g_return_val_if_fail (GTK_IS_EVENT_CONTROLLER_KEY (controller), NULL);
+
+  return controller->im_context;
+}
diff --git a/gtk/gtkeventcontrollerkey.h b/gtk/gtkeventcontrollerkey.h
new file mode 100644 (file)
index 0000000..025aa33
--- /dev/null
@@ -0,0 +1,57 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2017, Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s): Carlos Garnacho <carlosg@gnome.org>
+ */
+
+#ifndef __GTK_EVENT_CONTROLLER_KEY_H__
+#define __GTK_EVENT_CONTROLLER_KEY_H__
+
+#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
+#error "Only <gtk/gtk.h> can be included directly."
+#endif
+
+#include <gdk/gdk.h>
+#include <gtk/gtkeventcontroller.h>
+#include <gtk/gtkimcontext.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_EVENT_CONTROLLER_KEY         (gtk_event_controller_key_get_type ())
+#define GTK_EVENT_CONTROLLER_KEY(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_EVENT_CONTROLLER_KEY, GtkEventControllerKey))
+#define GTK_EVENT_CONTROLLER_KEY_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_EVENT_CONTROLLER_KEY, GtkEventControllerKeyClass))
+#define GTK_IS_EVENT_CONTROLLER_KEY(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_EVENT_CONTROLLER_KEY))
+#define GTK_IS_EVENT_CONTROLLER_KEY_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_EVENT_CONTROLLER_KEY))
+#define GTK_EVENT_CONTROLLER_KEY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_EVENT_CONTROLLER_KEY, GtkEventControllerKeyClass))
+
+typedef struct _GtkEventControllerKey GtkEventControllerKey;
+typedef struct _GtkEventControllerKeyClass GtkEventControllerKeyClass;
+
+GDK_AVAILABLE_IN_3_24
+GType               gtk_event_controller_key_get_type  (void) G_GNUC_CONST;
+
+GDK_AVAILABLE_IN_3_24
+GtkEventController *gtk_event_controller_key_new (GtkWidget *widget);
+
+GDK_AVAILABLE_IN_3_24
+void                gtk_event_controller_key_set_im_context (GtkEventControllerKey *controller,
+                                                             GtkIMContext          *im_context);
+GDK_AVAILABLE_IN_3_24
+GtkIMContext *      gtk_event_controller_key_get_im_context (GtkEventControllerKey *controller);
+
+G_END_DECLS
+
+#endif /* __GTK_EVENT_CONTROLLER_KEY_H__ */
index a2cd6f5c867e5e9a8fdd462783fb351d34fb3367..443d15a77d0803c59e0c731d07d1ed398473b96d 100644 (file)
@@ -142,6 +142,9 @@ gtk/gtkentry.c
 gtk/gtkentrycompletion.c
 gtk/gtkeventbox.c
 gtk/gtkeventcontroller.c
+gtk/gtkeventcontrollerkey.c
+gtk/gtkeventcontrollermotion.c
+gtk/gtkeventcontrollerscroll.c
 gtk/gtkexpander.c
 gtk/gtkfilechooserbutton.c
 gtk/gtkfilechooser.c